feat: EXPIRETIME, PEXPIRETIME, SMOVE, and SINTERCARD - #315
Merged
Conversation
adds four new Redis-compatible commands: - EXPIRETIME key — returns absolute Unix timestamp (seconds) when the key expires; -1 if no expiry, -2 if the key doesn't exist - PEXPIRETIME key — same but in milliseconds - SMOVE src dst member — atomically moves a member between sets; handles cross-shard keys by doing a srem + sadd across shards - SINTERCARD numkeys key... [LIMIT n] — returns the cardinality of the intersection of N sets with an optional cap; fetches members from each key's owning shard before computing the intersection in the execute layer the internal expiry clock is monotonic (ms since process start), so a new monotonic_to_unix_ms() helper in time.rs anchors it to wall-clock time once and uses fast arithmetic on every subsequent call. AOF persistence: SMOVE on a single shard writes two records (SRem + SAdd) which replay cleanly; cross-shard SMOVE persists each sub-command to its own shard's AOF log. 12 integration tests, unit tests for smove and sintercard in keyspace/set.rs.
kacy
added a commit
that referenced
this pull request
Feb 27, 2026
adds command metadata for all commands from prs #315–319: - generic: expireat, expiretime, pexpireat, pexpiretime - string: getset, msetnx - bitmap (new group): bitcount, bitop, bitpos, getbit, setbit - list: lmpop; update lpop/rpop args to include optional count - hash: hrandfield - set: sintercard, smove - sorted_set: zmpop, zrandmember all entries are alphabetically ordered within their group.
kacy
added a commit
that referenced
this pull request
Feb 27, 2026
…320) * feat(cli): surface 17 new commands in autocomplete and help adds command metadata for all commands from prs #315–319: - generic: expireat, expiretime, pexpireat, pexpiretime - string: getset, msetnx - bitmap (new group): bitcount, bitop, bitpos, getbit, setbit - list: lmpop; update lpop/rpop args to include optional count - hash: hrandfield - set: sintercard, smove - sorted_set: zmpop, zrandmember all entries are alphabetically ordered within their group. * feat(grpc): add 17 new rpcs — bitmap, expireat, getset, smove, lmpop, zmpop, hrandfield, zrandmember * feat(clients): add 17 new commands — bitmap, expireat, getset, smove, lmpop, zmpop, hrandfield, zrandmember
kacy
added a commit
that referenced
this pull request
Feb 27, 2026
marks GETSET, GETDEL, GETEX, MSETNX, WAIT, EXPIREAT, PEXPIREAT, EXPIRETIME, PEXPIRETIME, LMOVE, LMPOP, HRANDFIELD, SMOVE, SINTERCARD, ZUNION, ZINTER, ZDIFF, ZRANDMEMBER, ZMPOP as implemented in the compatibility guide. adds a new bitmap section for BITCOUNT, GETBIT, SETBIT, BITOP, BITPOS. ember-client readme gains new rows for all 17 commands from prs #315-319, a bitmaps section, and correct expiry/random-field signatures. also fixes a clippy error (approximate pi literal in a unit test) and two assert_eq!(bool) warnings in the client crate.
kacy
added a commit
that referenced
this pull request
Feb 27, 2026
the go client was failing to compile because pb/ stubs were stale — missing the 17 rpcs added in prs #315–319 (expiretime, pexpiretime, expireat, pexpireat, getset, msetnx, getbit, setbit, bitcount, bitpos, bitop, smove, sintercard, lmpop, zmpop, hrandfield, zrandmember). regenerated with `make proto-gen` from clients/ember-go/.
kacy
added a commit
that referenced
this pull request
Feb 27, 2026
the go client was failing to compile because pb/ stubs were stale — missing the 17 rpcs added in prs #315–319 (expiretime, pexpiretime, expireat, pexpireat, getset, msetnx, getbit, setbit, bitcount, bitpos, bitop, smove, sintercard, lmpop, zmpop, hrandfield, zrandmember). regenerated with `make proto-gen` from clients/ember-go/.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
summary
adds four Redis-compatible commands that cover common expiry-inspection and
set-manipulation patterns:
key will expire (seconds and milliseconds respectively). internally the store
uses a monotonic clock, so a new
monotonic_to_unix_mshelper intime.rsanchors it to wall-clock time on first call and uses arithmetic only after that.
source and destination land on the same shard the operation is a single
keyspace call; when they differ, execute.rs does a
SRemthenSAddacrossthe two shards.
with an optional upper bound. each key is fetched from its owning shard via
SMembers, and the intersection is computed in the execute layer so cross-shardkey sets work correctly.
AOF persistence follows the same pattern as the rest of the codebase: SMOVE on a
single shard records two entries (SRem + SAdd); for cross-shard SMOVE, each
sub-request persists to its own shard's log. EXPIRETIME and PEXPIRETIME are
read-only and produce no AOF records.
what was tested
cargo test --test integration— 135 tests pass, including 12 new tests:expiretime_returns_absolute_epoch,expiretime_no_expiry_returns_minus_one,expiretime_missing_key_returns_minus_twopexpiretime_precision,pexpiretime_no_expiry_returns_minus_one,pexpiretime_missing_key_returns_minus_twosmove_basic,smove_missing_member_returns_zero,smove_missing_source_returns_zerosintercard_basic,sintercard_with_limit,sintercard_missing_key_returns_zerocargo clippy -p ember-protocol -p emberkv-core -p ember-server -- -D warnings— cleankeyspace/set.rsfor smove and sintercard edge casesdesign notes
the cross-shard SMOVE is non-atomic in the distributed sense — a crash between
the SRem and SAdd steps would leave the member only in the source set. this is
the same trade-off made by RENAME and COPY for cross-shard keys and is acceptable
for a single-node store. if strong atomicity across shards is ever needed, the
right path is a two-phase write log, not adding complexity now.